/**
	Pulsar engine.
	Additional include file. Own lists

	Copyright (C) 2013-2014 Dmitry Dolgov

	This program is free software; you can redistribute it and/or modify it under the terms of the
	GNU General Public License as published by the Free Software Foundation; either version 2 of
	the License, or (at your option) any later version.

	This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
	without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
	See the GNU General Public License for more details.
*/

namespace pl
{

template<typename T> class List;


template<typename T> class ListNode
{
	template<class Y> friend class List;

public:
	/// Ctor
	inline explicit ListNode(List<T>& list) :
	_list(NULL),
	_prev(NULL),
	_next(NULL)
	{
		list.add(this);
		return;
	}

	/// Dtor
	virtual ~ListNode()
	{
		_list->remove(this);
		return;
	}

	/// Next item
	inline ListNode* next()
	{
		return _next;
	}

	/// Next const item
	inline const ListNode* next() const
	{
		return _next;
	}

	/// Prev item
	inline ListNode* prev()
	{
		return _prev;
	}

	/// Prev const item
	inline const ListNode* prev() const
	{
		return _prev;
	}

	/// List owner
	inline List<T>* owner()
	{
		return _list;
	}

	/// List const owner
	inline const List<T>* owner() const
	{
		return _list;
	}

protected:
	List<T>* _list;

private:
	ListNode* _prev;
	ListNode* _next;
};

template<typename T> class List
{
public:
	/// Ctor
	explicit inline List()
		: _head(NULL)
		, _tail(NULL)
		, _size(0)
	{
		return;
	}

	/// Dror
	virtual ~List()
	{
		PL_ASSERTW(_size == 0, PL_T("Some objects was not deleted from list"));
		deleteAllObjects();
		return;
	}

	/// Add new item
	inline void add(ListNode<T>* value)
	{
		if (value == NULL)
		{
			PL_ASSERTW(FALSE, PL_T("Wrong arguments"));
			return;
		}
		// Remove item from enother list (if present)
		if (value->_list != NULL)
		{
			value->_list->remove(value);
		}
		// Adding to list
		if (_head == NULL)
		{
			PL_ASSERTW(_tail == NULL, PL_T("Internal List error"));
			_head = _tail = value;
		}
		else
		{
			_tail->_next = value;
			value->_prev = _tail;
			_tail = value;
		}
		value->_list = this;
		++_size;
		return;
	}

	/// Remove item from list
	inline void remove(ListNode<T>* value)
	{
		if (value == NULL)
		{
			PL_ASSERTW(FALSE, PL_T("Wrong arguments"));
			return;
		}
		if (value->_list != this)
		{
			PL_ASSERTW(FALSE, PL_T("Item is not a member of this list"));
			return;
		}

		if (value == _head)
		{
			_head = _head->_next;
		}

		if (value == _tail)
		{
			_tail = _tail->_prev;
		}

		if (value->_next != NULL)
		{
			value->_next->_prev = value->_prev;
		}

		if (value->_prev != NULL)
		{
			value->_prev->_next = value->_next;
		}

		value->_next = value->_prev = NULL;
		value->_list = NULL;
		--_size;
		return;
	}

	/// Remove all items from list
	inline void deleteAllObjects()
	{
		ListNode<T>* pCur = _head;
		while(_head != NULL)
		{
			pCur = _head;
			PL_DELETE pCur;
		}
		PL_ASSERTW(_size == 0, PL_T("Internal List error"));
	}

	/// Get item count
	inline pl_uint32 size() const
	{
		return _size;
	}

	/// Get head
	inline ListNode<T>* nodeHead()
	{
		return _head;
	}

	/// Get const head
	inline const ListNode<T>* nodeHead() const
	{
		return _head;
	}

	/*
	TODO ???
	/// Get head item
	inline T* head()
	{
		return static_cast<T*>(_head);
	}

	/// Get const head item
	inline const T* head() const
	{
		return static_cast<const T*>(_head);
	}

	/// Get tail
	inline T* tail()
	{
		return static_cast<T*>(_tail);
	}

	/// Get tail (const version)
	inline const T* tail() const
	{
		return static_cast<const T*>(_tail);
	}
	*/

	/// Get tail
	inline ListNode<T>* nodeTail()
	{
		return _tail;
	}

	/// Get tail (const version)
	inline const ListNode<T>* nodeTail() const
	{
		return _tail;
	}

private:
	ListNode<T>* _head;
	ListNode<T>* _tail;
	pl_uint32 _size;
};

/**
	Enumerate all items in list
*/
template<typename T> class ListEnumerator
{
private:
	ListNode<T>* _ptr;
	bool _first;

public:
	/// Ctor
	explicit ListEnumerator(List<T>& list) :
	_ptr(list.nodeHead()),
	_first(true)
	{
	}

	T* operator -> () const
	{
		return static_cast<T*>(_ptr);
	}

	operator T* () const
	{
		return static_cast<T*>(_ptr);
	}

	bool isFinished()
	{
		if (_first)
		{
			_first = false;
		}
		else if (_ptr != NULL)
		{
			_ptr = _ptr->next();
		}
		else
		{
			PL_ASSERTW(false, PL_T("ListEnumeration was finished"));
		}
		return _ptr == NULL;
	}

	/// Restart enumeration
	void reset(List<T>& list)
	{
		_ptr	= static_cast<T*>(list.nodeHead());
		_first	= true;
	}
};

/**
	Enumerate all items in list (const version)
*/
template<typename T> class ListConstEnumerator
{
private:
	const ListNode<T>* _ptr;
	bool _first;

public:
	/// Ctor
	explicit ListConstEnumerator(const List<T>& list) :
	_ptr(list.nodeHead()),
	_first(true)
	{
	}

	const T* operator -> () const
	{
		return static_cast<const T*>(_ptr);
	}

	operator const T* () const
	{
		return static_cast<const T*>(_ptr);
	}

	bool isFinished()
	{
		if (_first)
		{
			_first = false;
		}
		else if (_ptr != NULL)
		{
			_ptr = _ptr->next();
		}
		else
		{
			PL_ASSERTW(false, PL_T("ListEnumeration was finished"));
		}
		return _ptr == NULL;
	}

	/// Restart enumeration
	void reset(const List<T>& list)
	{
		_ptr	= list.nodeHead();
		_first	= true;
	}
};

/**
	Enumerate all items in list in reverse order
*/
template<typename T> class ListReverseEnumerator
{
private:
	ListNode<T>* _ptr;
	bool _first;

public:
	/// Ctor
	explicit ListReverseEnumerator(List<T>& list) :
	_ptr(list.nodeTail()),
	_first(true)
	{}

	T* operator -> () const
	{
		return static_cast<T*>(_ptr);
	}

	operator T* () const
	{
		return static_cast<T*>(_ptr);
	}

	bool isFinished()
	{
		if (_first)
		{
			_first = false;
		}
		else if (_ptr != NULL)
		{
			_ptr = _ptr->prev();
		}
		else
		{
			PL_ASSERTW(false, PL_T("ListEnumeration was finished"));
		}
		return _ptr == NULL;
	}

	/// Restart enumerator
	void reset(List<T>& list)
	{
		_ptr	= list.nodeTail();
		_first	= true;
	}
};

/**
	Enumerate all items in list in reverse order (const version)
*/
template<typename T> class ListConstReverseEnumerator
{
private:
	const ListNode<T>* _ptr;
	bool _first;

public:
	/// Ctor
	explicit ListConstReverseEnumerator(const List<T>& list) :
	_ptr(list.nodeTail()),
	_first(true)
	{
	}

	const T* operator -> () const
	{
		return static_cast<const T*>(_ptr);
	}

	operator const T* () const
	{
		return static_cast<const T*>(_ptr);
	}

	bool isFinished()
	{
		if (_first)
		{
			_first = false;
		}
		else if (_ptr != NULL)
		{
			_ptr = _ptr->prev();
		}
		else
		{
			PL_ASSERTW(false, PL_T("ListEnumeration was finished"));
		}
		return _ptr == NULL;
	}

	/// Restart enumerator
	void reset(const List<T>& list)
	{
		_ptr	= list.nodeTail();
		_first	= true;
	}
};

} // namespace pl

//
// End of file 'pulsar_list.h'
//
